home *** CD-ROM | disk | FTP | other *** search
/ Gigarom 1 / Gigarom Macintosh Archives (Quantum Leap)(CDRM1080320)(1993).iso / FILES / DEV / I-Z / TransSkel.cpt / TransEdit.pas < prev    next >
Pascal/Delphi Source File  |  1987-02-25  |  41KB  |  1,561 lines

  1. {    TransEdit.c version 1.0 - TransSkel plug-in module supporting an}
  2. {    arbitrary number of generic edit windows.  Each window may be}
  3. {    bound to a file.}
  4.  
  5. {    *** Requires FakeAlert.pas for proper linking! ***}
  6.  
  7. {    Shortcomings:}
  8. {        Doesn't check for the obvious out of memory conditions.}
  9.  
  10. {    TransSkel and TransEdit are public domain, and are written by:}
  11.  
  12. {            Paul DuBois}
  13. {            Wisconsin Regional Primate Research Center}
  14. {            1220 Capital Court}
  15. {            Madison WI  53706  USA}
  16.  
  17. {    UUCP:    {allegra,ihnp4,seismo}
  18. {    The Pascal Version of TransSkel is public domain and was ported by        }
  19.  
  20. {            Owen Hartnett            }
  21. {            Ωhm Software            }
  22. {            163 Richard Drive        }
  23. {            Tiverton, RI 02878        }
  24.  
  25.  
  26. {    CSNET:    omh@cs.brown.edu.CSNET                                             }
  27. {    ARPA:        omh%cs.brown.edu@relay.cs.net-relay.ARPA                        }
  28. {    UUCP:        [ihnp4,allegra]!brunix !omh                                            }
  29.  
  30. { Unlike the C version of TransEdit, this version does not support}
  31. { a single window mode.  Because there are no conditional compile directives in}
  32. { LightSpeed Pascal, this was not possible}
  33.  
  34. UNIT TransEditpas;
  35.  
  36. INTERFACE
  37.     USES
  38.         FakeAlert, TransSkelPas;
  39.     TYPE
  40.         SFReplyPtr = ^SFReply;
  41.  
  42.     FUNCTION EWindowClose (theWind : WindowPtr) : boolean;
  43.     FUNCTION IsEWindow (theWind : WindowPtr) : Boolean;
  44.     FUNCTION IsEWindowDirty (theWind : WindowPtr) : Boolean;
  45.     FUNCTION GetEWindowTE (theWind : WindowPtr) : TEHandle;
  46.     FUNCTION GetEWindowFile (theWind : WindowPtr;
  47.                                     fileInfo : SFReplyPtr) : Boolean;
  48.     PROCEDURE SetEWindowProcs (theWind : WindowPtr;
  49.                                     pKey, pActivate, pClose : ProcPtr);
  50.     PROCEDURE SetEWindowStyle (theWind : WindowPtr;
  51.                                     font, size, wrap, just : integer);
  52.     PROCEDURE EWindowOverhaul (theWind : WindowPtr;
  53.                                     showCaret, recalc, dirty : Boolean);
  54.     PROCEDURE EWindowEditOp (item : integer);
  55.     PROCEDURE SetEWindowCreator (creat : OSType);
  56.     FUNCTION EWindowSave (theWind : WindowPtr) : Boolean;
  57.     FUNCTION EWindowSaveAs (theWind : WindowPtr) : Boolean;
  58.     FUNCTION EWindowSaveCopy (theWind : WindowPtr) : Boolean;
  59.     FUNCTION EWindowRevert (theWind : WindowPtr) : Boolean;
  60.     FUNCTION NewEWindow (bounds : Rect;
  61.                                     title : Str255;
  62.                                     visible : Boolean;
  63.                                     behind : WindowPtr;
  64.                                     goAway : Boolean;
  65.                                     refNum : longint;
  66.                                     bindToFile : Boolean) : WindowPtr;
  67.     FUNCTION ClobberEWindows : Boolean;
  68.     PROCEDURE TransEditInit;
  69.  
  70. IMPLEMENTATION
  71.     CONST
  72.  
  73. {    Edit window types, constants, variables.}
  74.  
  75.         enter = 3;
  76.         cr = 13;
  77.         monaco = 4;
  78.         shiftKey = $200;
  79.  
  80.             { Edit menu item numbers }
  81.  
  82.         undo = 1;
  83.         cut = 3;
  84.         copy = 4;
  85.         paste = 5;
  86.         clear = 6;        { (it's ok if the host doesn't have this item) }
  87.  
  88. {    ewList points to a list of structures describing the known edit}
  89. {    windows.}
  90.  
  91.     TYPE
  92.         EIptr = ^EditInfoRec;
  93.         EIHandle = ^EIPtr;
  94.         EditInfoRec = RECORD
  95.                 editWind : WindowPtr;
  96.                 bound : Boolean;
  97.                 editFile : SFReply;
  98.                 editTE : TEHandle;
  99.                 dirty : Boolean;
  100.                 scroll : ControlHandle;
  101.                 visLines : integer;
  102.                 eKey, eActivate, eClose : ProcPtr;
  103.                 eNext : EIHandle;
  104.             END;
  105.  
  106.  
  107.     VAR
  108.         e_font, e_size, e_wrap, e_just : integer;
  109.         e_key, e_activate, e_close : ProcPtr;
  110.         ewList : EIHandle;
  111.  
  112. {    Global variables - most of these are always synced to}
  113. {    the current window.  Note that not all these are set by}
  114. {    SyncGlobals, since some are not often needed.  When they}
  115. {    are all needed, use SyncAllGlobals.}
  116.  
  117.         editInfo : EIHandle;                { window's info structure      }
  118.         editWind : WindowPtr;            { the window                   }
  119.         editTE : TEHandle;                { window text                  }
  120.         editScroll : ControlHandle;        { the scroll bar               }
  121.         editFile : SFReply;                { file information             }
  122.         visLines : integer;                { number of lines in window    }
  123.         bound, dirty : Boolean;            { true if window bound to file }
  124.                                         { whether window is dirty      }
  125.         eKey, eActivate, eClose : ProcPtr;            { key click notifier           }
  126.                                                     { activate event notifier      }
  127.                                                     { close notifier               }
  128.         windID : integer;
  129.         dlogWhere : Point;                { GetFile/PutFile location }
  130.         creator : OSType;                { default file creator }
  131.  
  132.         clipRgn : RgnHandle;
  133.  
  134.     PROCEDURE TransEditInit;
  135.  
  136. {Extra routine to do initialization of variables, LSP can't do this }
  137.  
  138.     BEGIN
  139.  
  140. {    Default values for edit window text display characteristics}
  141. {    and event notification procedures}
  142.  
  143.         e_font := monaco;    { default font                 }
  144.         e_size := 9;            { default pointsize            }
  145.         e_wrap := 0;            { default word wrap (on)       }
  146.         e_just := teJustLeft;{ default justification        }
  147.         e_key := NIL;        { default key procedure        }
  148.         e_activate := NIL;    { default activation procedure }
  149.         e_close := NIL;        { default close procedure      }
  150.         ewList := NIL;
  151.         editWind := NIL;
  152.         windID := 0;
  153.         dlogWhere.v := 70;
  154.         dlogWhere.h := 100;
  155.         creator := 'TEDT';
  156.     END;
  157.  
  158. { -------------------------------------------------------------------- }
  159. {                Miscellaneous Internal (private) Routines                }
  160. { -------------------------------------------------------------------- }
  161.  
  162.  
  163.  
  164. {    Save and restore the current window's clip region}
  165.  
  166.     PROCEDURE SaveClipRgn;
  167.     BEGIN
  168.         clipRgn := NewRgn;
  169.         GetClip(clipRgn);
  170.     END;
  171.  
  172.     PROCEDURE RestoreClipRgn;
  173.     BEGIN
  174.         SetClip(clipRgn);
  175.         DisposeRgn(clipRgn);
  176.     END;
  177.  
  178. {    Draw grow box in lower right hand corner of window.}
  179.  
  180.     PROCEDURE DrawGrowBox;
  181.  
  182.         VAR
  183.             r : Rect;
  184.  
  185.     BEGIN
  186.         SaveClipRgn;
  187.         r := editWind^.portRect;
  188.         r.left := r.right - 15;
  189.         r.top := r.bottom - 15;        { draw only in corner }
  190.         ClipRect(r);
  191.         DrawGrowIcon(editWind);
  192.         RestoreClipRgn;
  193.     END;
  194.  
  195. { -------------------------------------------------------------------- }
  196. {            Lowest-level Internal (Private) Edit Window Routines        }
  197. { -------------------------------------------------------------------- }
  198.  
  199. {    Get edit window info associated with window.}
  200. {    Return nil if window isn't a known edit window.}
  201.  
  202.     FUNCTION GetEInfo (theWind : WindowPtr) : EIHandle;
  203.  
  204.         VAR
  205.             h : EIHandle;
  206.             foundflag : Boolean;
  207.  
  208.     BEGIN
  209.         h := ewList;
  210.         foundflag := false;                        { set to true when window found !}
  211.         WHILE h <> NIL DO
  212.             BEGIN
  213.                 IF h^^.editWind = theWind THEN
  214.                     BEGIN
  215.                         GetEInfo := h;
  216.                         h := NIL;
  217.                         foundflag := true;
  218.                     END
  219.                 ELSE
  220.                     h := h^^.eNext;
  221.             END;
  222.         IF foundflag = false THEN
  223.             GetEInfo := NIL;
  224.     END;
  225.  
  226. {    Synchronize globals to an edit window and make it the}
  227. {    current port.  theWind must be a legal edit window, with one}
  228. {    exception:  if theWind is nil, the variables are synced to the}
  229. {    port that's already current.  That is safe (and correct) because:}
  230.  
  231. {    (i)     nil is only passed by edit window handler procedures,}
  232. {         which are only attached to edit windows}
  233. {    (ii) TransSkel always sets the port to the window before}
  234. {         calling the handler proc.}
  235.  
  236. {    Hence, using the current port under these circumstances always}
  237. {    produces a legal edit window.}
  238.  
  239.     PROCEDURE SyncGlobals (theWind : WindowPtr);
  240.  
  241.     BEGIN
  242.         IF theWind = NIL THEN                    { use current window }
  243.             GetPort(theWind);
  244.         SetPort(theWind);
  245.         editWind := theWind;
  246.         editInfo := GetEInfo(editWind);
  247.         editTE := editInfo^^.editTE;
  248.         editScroll := editInfo^^.scroll;
  249.         visLines := editInfo^^.visLines;
  250.     END;
  251.  
  252.     PROCEDURE SyncAllGlobals (theWind : WindowPtr);
  253.  
  254.     BEGIN
  255.         SyncGlobals(theWind);                { sync display globals }
  256.         editFile := editInfo^^.editFile;
  257.         bound := editInfo^^.bound;            { procedure globals }
  258.         dirty := editInfo^^.dirty;
  259.         eKey := editInfo^^.eKey;
  260.         eActivate := editInfo^^.eActivate;
  261.         eClose := editInfo^^.eClose;
  262.     END;
  263.  
  264. {    Set dirty flag for current window}
  265.  
  266.     PROCEDURE SetDirty (boolVal : Boolean);
  267.  
  268.     BEGIN
  269.         editInfo^^.dirty := boolVal;
  270.     END;
  271.  
  272. { -------------------------------------------------------------------- }
  273. {                    Internal (private) Display Routines                    }
  274. { -------------------------------------------------------------------- }
  275.  
  276. {    Calculate the dimensions of the editing rectangle for}
  277. {    editWind (which must be set properly and is assumed to be}
  278. {    the current port).  (The viewRect and destRect are the}
  279. {    same size.)  Assumes the port, text font and text size are all}
  280. {    set properly.  The viewRect is sized so that an integral}
  281. {    number of lines can be displayed in it, i.e., so that a}
  282. {    partial line never shows at the bottom.  If that's not}
  283. {    done, funny things can happen to the caret.}
  284.  
  285.     PROCEDURE GetEditRect (VAR r : Rect);
  286.  
  287.         VAR
  288.             f : FontInfo;
  289.             lineHeight : integer;
  290.  
  291.     BEGIN
  292.         GetFontInfo(f);
  293.         lineHeight := f.ascent + f.descent + f.leading;
  294.         r := editWind^.portRect;
  295.         r.left := r.left + 4;
  296.         r.right := r.right - 17;        { leave room for scroll bar }
  297.         r.top := r.top + 2;
  298.         r.bottom := r.top + ((r.bottom - r.top - 2) DIV lineHeight) * lineHeight;
  299.     END;
  300.  
  301. {    Set the edit rect properly.}
  302.  
  303.     PROCEDURE SetEditRect;
  304.  
  305.         VAR
  306.             r : Rect;
  307.  
  308.     BEGIN
  309.         GetEditRect(r);
  310.         editTE^^.destRect.right := r.right;
  311.         editTE^^.viewRect := r;
  312.     END;
  313.  
  314. {    Calculate the dimensions of the scroll bar rectangle for}
  315. {    editWind (which must be set properly).  Make sure that}
  316. {    the edges overlap the window frame and the grow box.}
  317.  
  318.     PROCEDURE CalcScrollRect (VAR r : Rect);
  319.  
  320.     BEGIN
  321.         r := editWind^.portRect;
  322.         r.right := r.right + 1;
  323.         r.top := r.top - 1;
  324.         r.left := r.right - 16;
  325.         r.bottom := r.bottom - 14;
  326.     END;
  327.  
  328. {    Return true if the mouse is in the non-scrollbar part of the}
  329. {    edit window.}
  330.  
  331.     FUNCTION PtInText (pt : Point) : Boolean;
  332.  
  333.         VAR
  334.             r : Rect;
  335.     BEGIN
  336.         r := editWind^.portrect;
  337.         r.right := r.right - 15;
  338.         PtInText := PtInRect(pt, r);
  339.     END;
  340.  
  341. {    Set the cursor appropriately.  If theCursor == iBeamCursor, check}
  342. {    that it's really in the text area of an edit window (and if not}
  343. {    set the cursor to an arrow instead).  Otherwise, set the cursor}
  344. {    to the given type (usually a watch).}
  345.  
  346. {    If the cursor is supposed to be set to an i-beam, it is assumed}
  347. {    that the globals are synced, because DoCursor changes them and}
  348. {    syncs them back.}
  349.  
  350. {    Pass -1 for theCursor to set the cursor to the arrow.}
  351.  
  352.     PROCEDURE DoCursor (theCursor : integer);
  353.  
  354.         VAR
  355.             pt : Point;
  356.             savePort : GrafPtr;
  357.             myCursor : CursHandle;
  358.  
  359.     BEGIN
  360.         IF theCursor = iBeamCursor THEN            { check whether there's an edit }
  361.             BEGIN                                        { window in front and if so,    }
  362.                 theCursor := -1;                            { whether the cursor's in its   }
  363.                 IF (IsEWindow(FrontWindow)) THEN        { text area                     }
  364.                     BEGIN
  365.                         GetPort(savePort);
  366.                         SyncGlobals(FrontWindow);
  367.                         GetMouse(pt);
  368.                         IF (PtInText(pt)) THEN
  369.                             theCursor := iBeamCursor;
  370.                         SyncGlobals(savePort);
  371.                     END;
  372.             END;
  373.         IF theCursor = -1 THEN
  374.             SetCursor(arrow)
  375.         ELSE
  376.             BEGIN
  377.                 myCursor := GetCursor(theCursor);
  378.                 SetCursor(myCursor^^);
  379.             END;
  380.     END;
  381.  
  382. {    Calculate the number of lines currently scrolled off}
  383. {    the top.}
  384.  
  385.     FUNCTION LinesOffTop : integer;
  386.         VAR
  387.             ePtr : TEPtr;
  388.     BEGIN
  389.         ePtr := editTE^;
  390.         LinesOffTop := ((ePtr^.viewRect.top - ePtr^.destRect.top) DIV ePtr^.lineHeight);
  391.     END;
  392.  
  393. {    Return the line number that the caret (or the beginning of}
  394. {    the currently selected text) is in.  Value returned is in}
  395. {    the range 0..(**editTE).nLines.  If = (**editTE).nLines, the}
  396. {    caret is past the last line.  The only special case to watch out}
  397. {{    for is when the caret is at the very end of the text.  If the}
  398. {    last character is not a carriage return, then the caret is on}
  399. {{    the (nLines-1)th line, not the (nLines)th line.}
  400. {{{    (This really should do a binary search for speed.){}
  401.  
  402.     FUNCTION LineWithCaret : integer;
  403.  
  404.         VAR
  405.             i, nLines, teLength, selStart, lineStart : integer;
  406.             doneflag : Boolean;
  407.             mychars : CharsHandle;
  408.  
  409.     BEGIN
  410.         selStart := editTE^^.selStart;
  411.         nLines := editTE^^.nLines;
  412.         teLength := editTE^^.teLength;
  413.  
  414.         IF (selStart = teLength) THEN
  415.             BEGIN
  416.                 mychars := TEGetText(editTE);
  417.                 IF (teLength <> 0) AND (mychars^^[teLength - 1] <> char(cr)) THEN
  418.                     LineWithCaret := nLines - 1
  419.                 ELSE
  420.                     LineWithCaret := nLines
  421.             END
  422.         ELSE
  423.             BEGIN
  424.                 i := 0;
  425.                 doneflag := false;            { Not done yet!     }
  426.                 WHILE NOT doneflag DO
  427.                     BEGIN
  428.                         lineStart := editTE^^.lineStarts[i];
  429.                         IF lineStart >= selStart THEN
  430.                             BEGIN
  431.                                 IF lineStart <> selStart THEN
  432.                                     i := i - 1;
  433.                                 LineWithCaret := i;
  434.                                 doneflag := true;
  435.                             END;
  436.                         i := i + 1;
  437.                     END;
  438.             END;
  439.     END;
  440.  
  441. {    Return the number of the last displayable line.  That's one}
  442. {    more than nLines if the text is empty or the last character}
  443. {    is a carriage return.}
  444.  
  445.     FUNCTION LastLine : integer;
  446.  
  447.         VAR
  448.             nLines, teLength : integer;
  449.             mychars : CharsHandle;
  450.  
  451.     BEGIN
  452.         nLines := editTE^^.nLines;
  453.         teLength := editTE^^.teLength;
  454.         myChars := TEGetText(editTE);
  455.         IF (mychars^^[teLength - 1] = char(cr)) OR (teLength = 0) THEN
  456.             nLines := nLines + 1;
  457.         LastLine := nLines;
  458.     END;
  459.  
  460. {    Set the maximum value of the scroll bar. }
  461.  
  462.     PROCEDURE SetScrollMax;
  463.  
  464.         VAR
  465.             topLines, scrollableLines, max : integer;
  466.  
  467.     BEGIN
  468.         topLines := LinesOffTop;
  469.         scrollableLines := LastLine - visLines;
  470.         IF topLines > scrollableLines THEN
  471.             max := topLines
  472.         ELSE
  473.             max := scrollableLines;
  474.  
  475.         IF max < 0 THEN
  476.             max := 0;
  477.  
  478.         IF max <> GetCtlMax(editScroll) THEN
  479.             BEGIN
  480.                 SetCtlMax(editScroll, max);
  481.                 IF max > 0 THEN
  482.                     HiliteControl(editScroll, 0)
  483.                 ELSE
  484.                     HiliteControl(editScroll, 255);
  485.             END;
  486.     END;
  487.  
  488. {    Set scroll bar current value (but only if it's different than}
  489. {    the current value, to avoid needless flashing).}
  490.  
  491.     PROCEDURE SetScrollValue (value : integer);
  492.  
  493.     BEGIN
  494.         IF GetCtlValue(editScroll) <> value THEN
  495.             SetCtlValue(editScroll, value);
  496.     END;
  497.  
  498. {    Scroll to the correct position.  lDelta is the}
  499. {    amount to CHANGE the current scroll setting by.}
  500.  
  501.     PROCEDURE ScrollText (lDelta : integer);
  502.  
  503.         VAR
  504.             topVisLine, newTopVisLine : integer;
  505.  
  506.     BEGIN
  507.         topVisLine := LinesOffTop;
  508.         newTopVisLine := topVisLine + lDelta;
  509.         IF newTopVisLine < 0 THEN                    { clip to range }
  510.             newTopVisLine := 0;
  511.         IF (newTopVisline > GetCtlMax(editScroll)) THEN
  512.             newTopVisLine := GetCtlMax(editScroll);
  513.         SetScrollValue(newTopVisLine);
  514.         TEScroll(0, (topVisLine - newTopVisLine) * editTE^^.lineHeight, editTE);
  515.     END;
  516.  
  517. {    Scroll to home position without redrawing.{}
  518.  
  519.     PROCEDURE ScrollToHome;
  520.         VAR
  521.             r : Rect;
  522.  
  523.     BEGIN
  524.         r := editTE^^.destRect;
  525.         OffsetRect(r, 0, 2 - r.top);
  526.         editTE^^.destRect := r;
  527.     END;
  528.  
  529. {    ClikLoop proc for autoscrolling text when the mouse is dragged out}
  530. {    of the text view rectangle.}
  531.  
  532. {    The clipping region has to be set to include the scroll bar,}
  533. {    because whenever this proc is called, TE has the region set down}
  534. {    to the view rectangle - if it's not reset, changes to the scroll}
  535. {    bar will not show up!}
  536.  
  537.     FUNCTION AutoScroll : Boolean;
  538.  
  539.         VAR
  540.             p : Point;
  541.             r : Rect;
  542.  
  543.     BEGIN
  544.         SaveClipRgn;
  545.         ClipRect(editWind^.portRect);
  546.         GetMouse(p);
  547.         r := editTE^^.viewRect;
  548.         IF (p.v < r.top) THEN
  549.             ScrollText(-1)
  550.         ELSE IF (p.v > r.bottom) THEN
  551.             ScrollText(1);
  552.         RestoreClipRgn;
  553.         AutoScroll := true;
  554.     END;
  555.  
  556. {    Filter proc for tracking mousedown in scroll bar.  The code for}
  557. {    the part originally hit is shoved into the control's reference}
  558. {    value by Mouse() before this is called.}
  559.  
  560. {    I suspect odd scrolling may occur for hits in paging regions if}
  561. {    the window is allowed to size such that less than two lines show.}
  562.  
  563.     PROCEDURE TrackScroll (theScroll : ControlHandle;
  564.                                     partCode : integer);
  565.  
  566.         VAR
  567.             lDelta : integer;
  568.  
  569.     BEGIN
  570.         IF partCode = GetCRefCon(theScroll) THEN    { still in same part? }
  571.             BEGIN
  572.                 CASE partCode OF
  573.                     inUpButton : 
  574.                         lDelta := -1;
  575.                     inDownButton : 
  576.                         lDelta := 1;
  577.                     inPageUp : 
  578.                         lDelta := -(visLines - 1);
  579.                     inPageDown : 
  580.                         lDelta := visLines - 1;
  581.                     OTHERWISE
  582.                 END;
  583.                 ScrollText(lDelta);
  584.             END;
  585.     END;
  586.  
  587. {    Set the scroll bar properly and adjust the text in the}
  588. {    window so that the line containing the caret is visible.}
  589. {    If the line with the caret if more than a line outside of}
  590. {    the viewRect, try to place it in the middle of the window.}
  591. {}
  592. {    Yes, it is necessary to SetScrollMax at the end.}
  593.  
  594.     PROCEDURE AdjustDisplay;
  595.         VAR
  596.             caretLine, topVisLine, d : integer;
  597.     BEGIN
  598.         SetScrollMax;
  599.         caretLine := LineWithCaret;
  600.         topVisLine := LinesOffTop;
  601.         d := caretLine - topVisLine;
  602.         IF d < 0 THEN
  603.             IF d = -1 THEN
  604.                 ScrollText(-1)
  605.             ELSE
  606.                 ScrollText(d - (visLines DIV 2))
  607.         ELSE
  608.             BEGIN
  609.                 d := caretLine - (topVisLine + visLines - 1);
  610.                 IF d > 0 THEN
  611.                     IF d = 1 THEN
  612.                         ScrollText(1)
  613.                     ELSE
  614.                         ScrollText(d + (visLines DIV 2))
  615.                 ELSE
  616.                     SetScrollValue(topVisLine);
  617.             END;
  618.         SetScrollMax;    { might have changed from scrolling }
  619.     END;
  620.  
  621. {    Overhaul the entire display.  This is called for major}
  622. {    catastrophes, such as resizing the window, or changes to}
  623. {    the word wrap style.  It makes sure the view and}
  624. {    destination rectangles are sized properly, and that the bottom}
  625. {    line of text never scrolls up past the bottom line of the}
  626. {    window, if there's enough to fill the window, and that the}
  627. {    scroll bar max and current values are set properly.}
  628.  
  629. {    Resizing the dest rect just means resetting the right edge}
  630. {    (the top is NOT reset), since text might be scrolled off the}
  631. {    top (i.e., destRect.top != 0).}
  632.  
  633. {    Doesn't redraw the control, though!}
  634.  
  635.     PROCEDURE OverhaulDisplay (showCaret : Boolean;
  636.                                     recalc : Boolean);
  637.  
  638.         VAR
  639.             r : Rect;
  640.     BEGIN
  641.         r := editTE^^.viewRect;
  642.         EraseRect(r);                { erase current viewRect }
  643.         SetEditRect;                { recalculate editing rects }
  644.         IF recalc THEN            { recalculate line starts }
  645.             TECalText(editTE);
  646.         visLines := (editTE^^.viewRect.bottom - editTE^^.viewRect.top) DIV editTE^^.lineHeight;
  647.         editInfo^^.visLines := visLines;
  648.  
  649.         IF showCaret THEN
  650.             AdjustDisplay
  651.         ELSE
  652.             SetScrollMax;
  653.         r := editTE^^.viewRect;
  654.         TEUpdate(r, editTE);
  655.     END;
  656.  
  657. { ---------------------------------------------------------------- }
  658. {                        Window Handler Routines                        }
  659. { ---------------------------------------------------------------- }
  660.  
  661.  
  662. {}
  663. {    Handle mouse clicks in window.  The viewRect is never tested}
  664. {    directly, because if it were, clicks along the top, left and}
  665. {    bottom edges of the window wouldn't register.}
  666.  
  667.     PROCEDURE Mouse (thePt : Point;
  668.                                     t : longint;
  669.                                     mods : integer);
  670.  
  671.         VAR
  672.             thePart, oldCtlValue, ignore : integer;
  673.  
  674.     BEGIN
  675.         SyncGlobals(NIL);                                { sync to current port }
  676.         thePart := TestControl(editScroll, thePt);
  677.         IF thePart = inThumb THEN
  678.             BEGIN
  679.                 oldCtlValue := GetCtlValue(editScroll);
  680.                 IF TrackControl(editScroll, thePt, NIL) = inThumb THEN
  681.                     ScrollText(GetCtlValue(editScroll) - oldCtlValue)
  682.             END
  683.         ELSE IF thePart <> 0 THEN
  684.             BEGIN
  685.                 SetCRefCon(editScroll, longint(thePart));
  686.                 ignore := TrackControl(editScroll, thePt, @TrackScroll);
  687.             END
  688.         ELSE IF (PtInText(thePt)) THEN
  689.             TEClick(thePt, BitAnd(mods, shiftKey) <> 0, editTE);
  690.         SetScrollMax;
  691.     END;
  692.  
  693.     PROCEDURE callpnoarg (myProc : ProcPtr);
  694.  
  695. { For all the Procedures that are called with no arguments                            }
  696.  
  697.     INLINE
  698.         $205f,     {movea.l  (a7)+,a0        ; (a0) is a ptr to string, 4(a0) is mode}
  699.         $4e90;
  700.  
  701.     PROCEDURE callpBoolean (myBool : Boolean;
  702.                                     myProc : ProcPtr);
  703.  
  704. { Two calls use Booleans as one parameter arguments.  This procedure handles    }
  705. { both of them.                                                                            }
  706.  
  707.     INLINE
  708.         $205f,     {movea.l  (a7)+,a0        ; (a0) is a ptr to string, 4(a0) is mode}
  709.         $4e90;
  710.  
  711. {    Handle key clicks in window}
  712.  
  713.     PROCEDURE Key (c : char;
  714.                                     mods : integer);
  715.  
  716.     BEGIN
  717.         SyncAllGlobals(NIL);                    { sync to current port }
  718.         IF c <> char(enter) THEN
  719.             TEKey(c, editTE);
  720.         AdjustDisplay;
  721.         SetDirty(true);
  722.         IF eKey <> NIL THEN                    { report event to the host }
  723.             callpnoarg(eKey);
  724.     END;
  725.  
  726. {    When the window comes active, highlight the scroll bar appropriately.}
  727. {    When the window is deactivated, un-highlight the scroll bar.}
  728. {    Redraw the grow box in any case.  Set the cursor (DoCursor avoids}
  729. {    changing it from an ibeam to an arrow back to an ibeam, in the case}
  730. {    where one edit window is going inactive and another is coming}
  731. {    active).}
  732. {}
  733. {    Report the event to the host.}
  734.  
  735.     PROCEDURE Activate (active : Boolean);
  736.  
  737.     BEGIN
  738.         SyncAllGlobals(NIL);        { sync to current port }
  739.         DrawGrowBox;
  740.         IF active THEN
  741.             BEGIN
  742.                 TEActivate(editTE);
  743.                 IF GetCtlMax(editScroll) > 0 THEN
  744.                     HiliteControl(editScroll, 0)
  745.                 ELSE
  746.                     HiliteControl(editScroll, 255);
  747.             END
  748.         ELSE
  749.             BEGIN
  750.                 TEDeactivate(editTE);
  751.                 HiliteControl(editScroll, 255);
  752.             END;
  753.         DoCursor(iBeamCursor);
  754.         IF (eActivate <> NIL) THEN    { report event to the host }
  755.             callpBoolean(active, eActivate);
  756.     END;
  757.  
  758. {    Close box was clicked.  If user specified notify proc, call it.}
  759. {    Otherwise do default close operation (ask about saving if dirty,}
  760. {    etc.).}
  761.  
  762.     PROCEDURE Close;
  763.  
  764.         VAR
  765.             ignore : integer;
  766.     BEGIN
  767.         SyncAllGlobals(NIL);        { sync to current port }
  768.         IF eclose <> NIL THEN
  769.             callpnoarg(eClose)
  770.         ELSE
  771.             ignore := integer(EWindowClose(editWind));
  772.     END;
  773.  
  774. {    Update window.  The update event might be in response to a}
  775. {    window resizing.  If so, move and resize the scroll bar.}
  776. {    The ValidRect call is done because the HideControl adds the}
  777. {    control bounds box to the update region - which would generate}
  778. {    another update event!  Since everything gets redrawn below,}
  779. {    the ValidRect is used to cancel the update.}
  780.  
  781.     PROCEDURE UpDate (resized : Boolean);
  782.  
  783.         VAR
  784.             r : Rect;
  785.  
  786.     BEGIN
  787.         SyncGlobals(NIL);        { sync to current port }
  788.         r := editWind^.portRect;
  789.         EraseRect(r);
  790.         IF resized THEN
  791.             BEGIN
  792.                 HideControl(editScroll);
  793.                 r := editScroll^^.contrlRect;
  794.                 ValidRect(r);
  795.                 CalcScrollRect(r);
  796.                 SizeControl(editScroll, 16, r.bottom - r.top);
  797.                 MoveControl(editScroll, r.left, r.top);
  798.                 OverhaulDisplay(false, editTE^^.crOnly >= 0);
  799.                 ShowControl(editScroll);
  800.             END
  801.         ELSE
  802.             BEGIN
  803.                 OverhaulDisplay(false, false);
  804.                 DrawControls(editWind);
  805.             END;
  806.         DrawGrowBox;
  807.     END;
  808.  
  809. {    Remove the edit window from the list, and dispose of it.}
  810. {    This is called by SkelRmveWind, not directly by user program.}
  811. {}
  812. {    At this point it's too late to back out if any changes have been}
  813. {    made to the text.}
  814.  
  815. {    Since the clobber procedure is never called except for real edit}
  816. {    windows, and since the list must therefore be non-empty, it is}
  817. {    not necessary to check the legality of the window or that the}
  818. {    window's in the list.}
  819.  
  820.     PROCEDURE Clobber;
  821.  
  822.         VAR
  823.             h, h2 : EIHandle;
  824.     BEGIN
  825.         SyncGlobals(NIL);                    { sync to current port }
  826.         IF ewList^^.editWind = editWind THEN    { is it the first window in list? }
  827.             BEGIN
  828.                 h2 := ewList;
  829.                 ewList := ewList^^.eNext;
  830.             END
  831.         ELSE
  832.             BEGIN
  833.                 h := ewList;
  834.                 WHILE h <> NIL DO
  835.                     BEGIN
  836.                         h2 := h^^.eNext;
  837.                         IF h2^^.editWind = editWind THEN    { found it }
  838.                             BEGIN
  839.                                 h^^.eNext := h2^^.eNext;
  840.                                 h := NIL;
  841.                             END;
  842.                         IF h <> NIL THEN
  843.                             h := h2;
  844.                     END;
  845.             END;
  846.         DisposHandle(Handle(h2));            { get rid of information structure }
  847.         TEDispose(editTE);                    { toss text record }
  848.         DisposeWindow(editWind);            { disposes of scroll bar, too }
  849.         editWind := NIL;
  850.         DoCursor(iBeamCursor);
  851.     END;
  852.  
  853. {    Blink the caret and make sure the cursor's an i-beam when it's}
  854. {    in the non-scrollbar part of the window.}
  855.  
  856.     PROCEDURE Idle;
  857.     BEGIN
  858.         SyncGlobals(NIL);
  859.         TEIdle(editTE);            { blink that caret! }
  860.         DoCursor(iBeamCursor);
  861.     END;
  862.  
  863. { ---------------------------------------------------------------- }
  864. {                        Internal File Routines                        }
  865. { ---------------------------------------------------------------- }
  866.  
  867.     PROCEDURE ErrMesg (s : Str255);
  868.         VAR
  869.             ignore : integer;
  870.     BEGIN
  871.         ignore := FakeAlert(s, '', '', '', 1, 1, 'OK', '', '');
  872.     END;
  873.  
  874. {    Save the contents of the edit window.  If there is no file bound}
  875. {    to the window, ask for a file name.  If askForFile is true, ask}
  876. {    for a name even if the window is currently bound to a file.  If}
  877. {    bindToFile is true, bind the window to the file written to (if}
  878. {    that's different than the currently bound file), and clear the}
  879. {    window's dirty flag.}
  880.  
  881. {    Return true if the file was written without error.  Return false}
  882. {    if (a) user was asked for name and clicked Cancel (b) there was}
  883. {    some error writing the file.  In the latter case, the window is}
  884. {    not bound to any new name given by user.}
  885.  
  886. {    Always returns false if the window isn't an edit window.  This}
  887. {    simplifies EWindowSave, EWindowSaveAs, EWindowSaveCopy.  (They}
  888. {    don't do the test.)}
  889.  
  890.     FUNCTION SaveFile (theWind : WindowPtr;
  891.                                     askForFile : Boolean;
  892.                                     bindToFile : Boolean) : Boolean;
  893.  
  894.         VAR
  895.             f : integer;
  896.             fndrInfo : FInfo;    { finder info }
  897.             tmpFile : SFReply;
  898.             hText : Handle;
  899.             count : longint;
  900.             result, ignore : OSErr;
  901.             haveNewFile, breakflag : Boolean;
  902.  
  903.     BEGIN
  904.         haveNewFile := false;
  905.         breakflag := false;                    { flag to detect a C 'return' statement    }
  906.         IF NOT IsEWindow(theWind) THEN
  907.             BEGIN
  908.                 SaveFile := false;
  909.                 breakflag := true;
  910.             END
  911.         ELSE
  912.             BEGIN
  913.                 SyncAllGlobals(theWind);
  914.                 tmpFile := editFile;
  915.                 IF (bound = false) OR askForFile THEN
  916.                     BEGIN
  917.                         SFPutFile(dlogWhere, 'Save File as:', editFile.fName, NIL, tmpFile);
  918.                         IF NOT tmpFile.good THEN
  919.                             BEGIN
  920.                                 SaveFile := false;
  921.                                 breakflag := true;
  922.                             END
  923.                         ELSE
  924.                             BEGIN
  925.                                 haveNewFile := true;
  926.                                 IF GetFInfo(tmpFile.fName, tmpFile.vRefNum, fndrInfo) = noErr THEN { exists }
  927.                                     BEGIN
  928.                                         IF fndrInfo.fdType <> 'TEXT' THEN
  929.                                             BEGIN
  930.                                                 ErrMesg('Not a TEXT File');
  931.                                                 SaveFile := false;
  932.                                                 breakflag := true;
  933.                                             END
  934.                                     END
  935.                                 ELSE    { doesn't exist.  create it. }
  936.                                     BEGIN
  937.                                         IF (Create(tmpFile.fName, tmpFile.vRefNum, creator, 'TEXT') <> noErr) THEN
  938.                                             BEGIN
  939.                                                 ErrMesg('Can''t Create');
  940.                                                 SaveFile := false;
  941.                                                 breakflag := true;
  942.                                             END;
  943.                                     END;
  944.                             END;
  945.                     END;
  946.             END;
  947.         IF NOT breakflag THEN
  948.             BEGIN
  949.                 IF FSOpen(tmpFile.fName, tmpFile.vRefNum, f) <> noErr THEN
  950.                     ErrMesg('Can''t Open')
  951.                 ELSE
  952.                     BEGIN
  953.                         DoCursor(watchCursor);
  954.                         ignore := SetFPos(f, fsFromStart, longint(0));
  955.                         hText := editTE^^.hText;
  956.                         HLock(hText);
  957.                         count := editTE^^.teLength;
  958.                         result := FSWrite(f, count, hText^);
  959.                         ignore := GetFPos(f, count);
  960.                         ignore := SetEOF(f, count);
  961.                         ignore := FSClose(f);
  962.                         ignore := FlushVol(NIL, tmpFile.vRefNum);
  963.                         HUnlock(hText);
  964.                         DoCursor(iBeamCursor);
  965.                         IF result = noerr THEN
  966.                             BEGIN
  967.                                 IF bindToFile THEN
  968.                                     BEGIN
  969.                                         SetDirty(false);
  970.                                         IF haveNewFile THEN
  971.                                             BEGIN
  972.                                                 SetWTitle(editWind, tmpFile.fName);
  973.                                                 editInfo^^.bound := true;
  974.                                                 editInfo^^.editFile := tmpFile;
  975.                                             END;
  976.                                     END;
  977.                                 SaveFile := true;
  978.                                 breakflag := true;
  979.                             END
  980.                         ELSE
  981.                             ErrMesg('Write error!');
  982.                     END;
  983.                 IF NOT breakflag THEN
  984.                     SaveFile := false;
  985.             END;
  986.     END;
  987.  
  988. {    Revert to version of file saved on disk.  Doesn't check whether}
  989. {    the window's really bound to a file or not, doesn't ask whether}
  990. {    to really revert if the window's dirty, does no redrawing, etc.}
  991. {    Just reports whether the file was read in successfully.}
  992.  
  993.     FUNCTION Revert : Boolean;
  994.  
  995.         VAR
  996.             result : Boolean;
  997.             f : integer;
  998.             len : longint;
  999.             h : Handle;
  1000.             ignore : OSErr;
  1001.  
  1002.     BEGIN
  1003.         result := false;
  1004.         DoCursor(watchCursor);
  1005.         IF FSOpen(editFile.fName, editFile.vRefNum, f) <> noErr THEN
  1006.             ErrMesg('Couldn''t open file')
  1007.         ELSE
  1008.             BEGIN
  1009.                 ignore := GetEOF(f, len);
  1010.                 IF len >= 32000 THEN
  1011.                     ErrMesg('File is too big')
  1012.                 ELSE
  1013.                     BEGIN
  1014.                         h := Handle(TEGetText(editTE));
  1015.                         SetHandleSize(h, len);
  1016.                         HLock(h);
  1017.                         ignore := FSRead(f, len, h^);
  1018.                         HUnlock(h);
  1019.                         editTE^^.teLength := len;
  1020.                         TESetSelect(longint(0), longint(0), editTE);    { set caret at start }
  1021.                         result := true;
  1022.                         SetDirty(false);
  1023.                     END;
  1024.                 ignore := FSClose(f);
  1025.             END;
  1026.         DoCursor(iBeamCursor);
  1027.         Revert := result;
  1028.     END;
  1029.  
  1030. { ------------------------------------------------------------ }
  1031. {            Lowest-level Interface (Public) Routines            }
  1032. { ------------------------------------------------------------ }
  1033.  
  1034.  
  1035. {}
  1036. {    Return true/false to indicate whether the window is really an}
  1037. {    edit window.}
  1038.  
  1039.     FUNCTION IsEWindow;
  1040.     BEGIN
  1041.         IsEWindow := GetEInfo(theWind) <> NIL;
  1042.     END;
  1043.  
  1044. {    Return true/false to indicate whether the text associated with}
  1045. {    the window has been changed since the last save/revert (or since}
  1046. {    created, if not bound to file).}
  1047.  
  1048.     FUNCTION IsEWindowDirty;
  1049.         VAR
  1050.             eInfo : EIHandle;
  1051.     BEGIN
  1052.         eInfo := GetEInfo(theWind);
  1053.         IF eInfo <> NIL THEN
  1054.             IsEWindowDirty := eInfo^^.dirty
  1055.         ELSE
  1056.             IsEwindowDirty := false;
  1057.     END;
  1058.  
  1059. {    Return a handle to the TextEdit record associated with the edit}
  1060. {    window, or nil if it's not an edit window}
  1061.  
  1062.     FUNCTION GetEWindowTE;
  1063.         VAR
  1064.             eInfo : EIHandle;
  1065.     BEGIN
  1066.         eInfo := GetEInfo(theWind);
  1067.         IF eInfo <> NIL THEN
  1068.             GetEWindowTE := eInfo^^.editTE
  1069.         ELSE
  1070.             GetEWindowTE := NIL;
  1071.     END;
  1072.  
  1073. {    Return true/false depending on whether the editor is bound to}
  1074. {    a file or not, and a copy of the file info in the second}
  1075. {    argument.  Pass nil for fileInfo if only want the return status.}
  1076. {    Returns false if it's not an edit window.}
  1077.  
  1078.     FUNCTION GetEWindowFile;
  1079.         VAR
  1080.             eInfo : EIHandle;
  1081.     BEGIN
  1082.         eInfo := GetEInfo(theWind);
  1083.         IF eInfo <> NIL THEN
  1084.             BEGIN
  1085.                 IF fileInfo <> NIL THEN
  1086.                     fileInfo^ := eInfo^^.editFile;
  1087.                 GetEWindowFile := eInfo^^.bound
  1088.             END
  1089.         ELSE
  1090.             GetEWindowFile := false;
  1091.     END;
  1092.  
  1093. { ---------------------------------------------------------------- }
  1094. {                    Interface Display Routines                        }
  1095. { ---------------------------------------------------------------- }
  1096.  
  1097.  
  1098. {}
  1099. {    Install event notification procedures for an edit window.}
  1100.  
  1101.     PROCEDURE SetEWindowProcs;
  1102.  
  1103.         VAR
  1104.             eInfo : EIHandle;
  1105.     BEGIN
  1106.         IF theWind = NIL THEN            { reset window creation defaults }
  1107.             BEGIN
  1108.                 e_key := pKey;
  1109.                 e_activate := pActivate;
  1110.                 e_close := pClose;
  1111.             END
  1112.         ELSE
  1113.             BEGIN
  1114.                 eInfo := GetEInfo(theWind);
  1115.                 IF eInfo <> NIL THEN
  1116.                     BEGIN
  1117.                         eInfo^^.eKey := pKey;
  1118.                         eInfo^^.eActivate := pActivate;
  1119.                         eInfo^^.eClose := pClose;
  1120.                     END;
  1121.             END;
  1122.     END;
  1123.  
  1124. {    Change the text display characteristics of an edit window}
  1125. {    and redisplay it.}
  1126.  
  1127. {    Scroll to home position before overhauling, because although}
  1128. {    the overhaul sets the viewRect to display an integral number}
  1129. {    of lines, there's no guarantee that the destRect offset will}
  1130. {    also be integral except at home position.  Clipping is set to}
  1131. {    an empty rect so the scroll doesn't show.}
  1132.  
  1133.     PROCEDURE SetEWindowStyle;
  1134.         VAR
  1135.             savePort : GrafPtr;
  1136.             f : FontInfo;
  1137.             te : TEHandle;
  1138.             r : Rect;
  1139.             oldWrap : integer;
  1140.  
  1141.     BEGIN
  1142.         IF theWind = NIL THEN            { reset window creation defaults }
  1143.             BEGIN
  1144.                 e_font := font;
  1145.                 e_size := size;
  1146.                 e_wrap := wrap;
  1147.                 e_just := just;
  1148.             END
  1149.         ELSE IF IsEWindow(theWind) THEN
  1150.             BEGIN
  1151.                 GetPort(savePort);
  1152.                 SyncGlobals(theWind);    { sync and set port }
  1153.                 te := editTE;
  1154.                 ScrollToHome;
  1155.  
  1156.                 oldWrap := te^^.crOnly;
  1157.                 te^^.crOnly := wrap;
  1158.                 TESetJust(just, te);    { set justification }
  1159.  
  1160.                 TextFont(font);         { set the font and point size }
  1161.                 TextSize(size);        { of text record }
  1162.                 GetFontInfo(f);
  1163.                 te^^.lineHeight := f.ascent + f.descent + f.leading;
  1164.                 te^^.fontAscent := f.ascent;
  1165.                 te^^.txFont := font;
  1166.                 te^^.txSize := size;
  1167.  
  1168.                 OverhaulDisplay(true, (oldWrap >= 0) OR (wrap >= 0));
  1169.  
  1170.                 SetPort(savePort);
  1171.             END;
  1172.     END;
  1173.  
  1174. {    Redo display.  Does not save current port.  This is used by hosts}
  1175. {    that mess with the text externally to TransEdit.  The arguments}
  1176. {    determine whether the text is scrolled to show the line with the}
  1177. {    caret, whether the lineStarts are recalculated, and whether the}
  1178. {    text should be marked dirty or not.}
  1179.  
  1180.     PROCEDURE EWindowOverhaul;
  1181.     BEGIN
  1182.         IF (IsEWindow(theWind)) THEN
  1183.             BEGIN
  1184.                 SyncGlobals(theWind);
  1185.                 OverhaulDisplay(showCaret, recalc);
  1186.                 DrawControls(editWind);
  1187.                 SetDirty(dirty);
  1188.             END;
  1189.     END;
  1190.  
  1191. { ---------------------------------------------------------------- }
  1192. {                        Menu Interface Routine                        }
  1193. { ---------------------------------------------------------------- }
  1194.  
  1195.  
  1196. {}
  1197. {    Do Edit menu selection.  This is only valid if an edit}
  1198. {    window is frontmost.}
  1199.  
  1200.     PROCEDURE EWindowEditOp;
  1201.  
  1202.         VAR
  1203.             ignore : integer;
  1204.     BEGIN
  1205.         IF IsEWindow(FrontWindow) THEN
  1206.             BEGIN
  1207.                 SyncGlobals(FrontWindow);
  1208.                 CASE item OF
  1209.  
  1210. {    cut selection, put in TE Scrap, clear clipboard and put}
  1211. {    TE scrap in it}
  1212.  
  1213.                     cut : 
  1214.                         BEGIN
  1215.                             TECut(editTE);
  1216.                             ignore := ZeroScrap;
  1217.                             ignore := TEToScrap;
  1218.                         END;
  1219.  
  1220. {    copy selection to TE Scrap, clear clipboard and put}
  1221. {    TE scrap in it}
  1222.  
  1223.                     copy : 
  1224.                         BEGIN
  1225.                             TECopy(editTE);
  1226.                             ignore := ZeroScrap;
  1227.                             ignore := TEToScrap;
  1228.                         END;
  1229.  
  1230. {    get clipboard into TE scrap, put TE scrap into edit record}
  1231.  
  1232.                     paste : 
  1233.                         BEGIN
  1234.                             ignore := TEFromScrap;
  1235.                             TEPaste(editTE);
  1236.                         END;
  1237.  
  1238. {    delete selection without putting into TE scrap or clipboard}
  1239.  
  1240.                     clear : 
  1241.                         TEDelete(editTE);
  1242.                     OTHERWISE
  1243.                 END;
  1244.                 AdjustDisplay;
  1245.                 SetDirty(true);
  1246.             END;
  1247.     END;
  1248.  
  1249. { ---------------------------------------------------------------- }
  1250. {                        Interface File Routines                        }
  1251. { ---------------------------------------------------------------- }
  1252.  
  1253.  
  1254. {}
  1255. {    Set file creator for any files created by TransEdit}
  1256.  
  1257.     PROCEDURE SetEWindowCreator;
  1258.     BEGIN
  1259.         creator := creat;
  1260.     END;
  1261.  
  1262. {    Save the contents of the given window}
  1263.  
  1264.     FUNCTION EWindowSave;
  1265.     BEGIN
  1266.         EWindowSave := SaveFile(theWind, false, true);    { window to save }
  1267.     { don't ask for file if have one }
  1268.     { bind to new file if one given }
  1269.  
  1270.     END;
  1271.  
  1272. {    Save the contents of the given window under a new name}
  1273. {    and bind to that name.}
  1274.  
  1275.     FUNCTION EWindowSaveAs;
  1276.     BEGIN
  1277.         EWindowSaveAs := SaveFile(theWind, true, true);{ window to save }
  1278.     { ask for file even if have one }
  1279.     { bind to new file if one given }
  1280.     END;
  1281.  
  1282. {    Save the contents of the given window under a new name, but}
  1283. {    don't bind to the name.}
  1284.  
  1285.     FUNCTION EWindowSaveCopy;
  1286.     BEGIN
  1287.         EWindowSaveCopy := SaveFile(theWind, true, false);    { window to save }
  1288.     { ask for file even if have one }
  1289.     { don't bind to file }
  1290.  
  1291.     END;
  1292.  
  1293. {    Close the window.  If it's dirty and is either bound to a file}
  1294. {    or (if not bound) has some text in it, ask about saving it first,}
  1295. {    giving user option of saving changes, tossing them, or}
  1296. {    cancelling altogether.}
  1297.  
  1298. {    Return true if the file was saved and the window closed, false if}
  1299. {    user cancelled or there was an error.}
  1300.  
  1301.     FUNCTION EWindowClose;
  1302.         VAR
  1303.             return : Boolean;
  1304.     BEGIN
  1305.         return := true;
  1306.         IF IsEWindow(theWind) = true THEN
  1307.             BEGIN
  1308.                 SyncAllGlobals(theWind);
  1309.                 IF ((bound OR (editTE^^.teLength > 0)) AND dirty) THEN
  1310.                     CASE (FakeAlert('Save changes to"', editFile.fName, '"?', '', 3, 3, 'Cancel', 'Discard', 'Save')) OF
  1311.                         1 :         { cancel Close }
  1312.                             return := false;
  1313.                         2 : 
  1314.                             ;             { toss changes }
  1315.                         3 : 
  1316.                             IF SaveFile(editWind, false, false) = false THEN        { window to save }
  1317.     { don't ask for name }
  1318.     { don't bind to name }
  1319.  
  1320.                                 return := false;    { cancelled or error - cancel Close }
  1321.                         OTHERWISE
  1322.                     END;
  1323.                 IF return THEN
  1324.                     SkelRmveWind(editWind);
  1325.                 EWindowClose := return;
  1326.             END;
  1327.     END;
  1328.  
  1329. {    Revert to saved version of file on disk.  theWind must be an edit}
  1330. {    window, and must be bound to a file.  Returns false if one of these}
  1331. {    conditions is not met, or if they are met but there was an error}
  1332. {    reading the file.}
  1333.  
  1334. {    The window need not be dirty, but if it is, the user is asked}
  1335. {    whether to really revert.}
  1336.  
  1337.     FUNCTION EWindowRevert;
  1338.         VAR
  1339.             return : Boolean;
  1340.     BEGIN
  1341.         return := true;
  1342.         IF NOT IsEWindow(theWind) THEN
  1343.             return := false
  1344.         ELSE
  1345.             BEGIN
  1346.                 SyncAllGlobals(theWind);
  1347.                 IF NOT bound THEN        { no file to revert to }
  1348.                     return := false
  1349.                 ELSE
  1350.                     BEGIN
  1351.                         IF dirty THEN
  1352.                             IF FakeAlert('"', editFile.fName, '" has been changed.  Really revert?', '', 2, 1, 'Cancel', 'Revert', '') = 1 THEN
  1353.                                 return := false;
  1354.                     END;
  1355.             END;
  1356.         IF return = true THEN
  1357.             IF Revert = false THEN
  1358.                 return := false;
  1359.         IF return = true THEN
  1360.             BEGIN
  1361.                 ScrollToHome;
  1362.                 OverhaulDisplay(true, true);
  1363.                 DrawControls(editWind);
  1364.                 ValidRect(editWind^.portRect);
  1365.             END;
  1366.         EWindowRevert := return;
  1367.     END;
  1368.  
  1369. { ---------------------------------------------------------------- }
  1370. {            Interface Initialization/Termination Routines            }
  1371. { ---------------------------------------------------------------- }
  1372.  
  1373.  
  1374. {}
  1375. {    Initialize the window and associated data structures.}
  1376. {    Return window pointer or nil if some sort of error.}
  1377. {}
  1378. {    Preserves the current port.}
  1379.  
  1380.     FUNCTION NewEWindow;
  1381.  
  1382.         VAR
  1383.             savePort : GrafPtr;
  1384.             r : Rect;
  1385.             mytype : SFTypeList;
  1386.             s, s2 : Str255;
  1387.             tPtr : STRING[64];
  1388.             eInfo : EIHandle;
  1389.             failure : Boolean;
  1390.     BEGIN
  1391.         mytype[0] := 'TEXT';
  1392.         failure := false;            {no failure yet!}
  1393.         IF bindToFile THEN
  1394.  
  1395. {    If supposed to bind to file, ask for name.  Return without doing}
  1396. {    anything if Cancel button clicked.}
  1397.  
  1398.             BEGIN
  1399.                 SFGetFile(dlogWhere, '', NIL, 1, myType, NIL, editFile);
  1400.                 IF NOT editFile.good THEN
  1401.                     failure := true
  1402.             END;
  1403.         IF NOT failure THEN
  1404.             BEGIN
  1405.                 bound := bindToFile;
  1406.                 IF bound THEN
  1407.  
  1408. {    Create window and install handler.  Set window title:  If window is}
  1409. {    to be bound to file, use name of file.  Otherwise use any title that}
  1410. {    was passed in.  If nil was passed, use a default name ("Untitled nnn").}
  1411. {    Also copy the name into the file info structure even if the window is}
  1412. {    unbound, because the Save operations expect to find it there as the}
  1413. {    most likely name to use if the window is untitled.}
  1414.  
  1415. {    Save and restore port, because it gets reset by the rest of the}
  1416. {    initialization code.}
  1417.  
  1418.                     tPtr := editFile.fName
  1419.                 ELSE
  1420.                     BEGIN
  1421.                         IF title <> '' THEN
  1422.                             tPtr := title
  1423.                         ELSE
  1424.                             BEGIN
  1425.                                 windId := windID + 1;                    { Who's says C is easier?  The C code for this }
  1426.                                 NumToString(longint(windID), s2);        { is ridiculous!!!!!                                    }
  1427.                                 tPtr := concat('Untitled ', s2);
  1428.                             END;
  1429.                         editFile.fName := tPtr;
  1430.                     END;
  1431.                 editWind := NewWindow(NIL, bounds, tPtr, false, documentProc, behind, goAway, refNum);
  1432.  
  1433.                 GetPort(savePort);
  1434.                 SkelWindow(editWind, @Mouse, @Key, @Update, @Activate, @Close, @Clobber, @Idle, true);
  1435.  
  1436.         { mouse click handler }
  1437.         { key click handler }
  1438.         { window updating procedure }
  1439.         { window activate/deactivate procedure }
  1440.         { window close procedure }
  1441.         { window disposal procedure }
  1442.         { idle proc }
  1443.         { idle only when frontmost }
  1444.  
  1445. {    Build the scroll bar.}
  1446.  
  1447.                 CalcScrollRect(r);
  1448.  
  1449.                 editScroll := NewControl(editWind, r, '', true, 0, 0, 0, scrollBarProc, longint(0));
  1450.  
  1451. {    Create the TE record used for text display.  Use default}
  1452. {    characteristics.}
  1453.  
  1454.                 GetEditRect(r);
  1455.                 editTE := TENew(r, r);
  1456.                 SetClikLoop(@AutoScroll, editTE);            { set autoscroll proc }
  1457.  
  1458. {    Get new information structure, attach to list of known edit}
  1459. {    windows.}
  1460.  
  1461.                 eInfo := EIHandle(NewHandle(Size(sizeof(EditInfoRec))));
  1462.                 editInfo := eInfo;
  1463.                 eInfo^^.eNext := ewList;
  1464.                 ewList := eInfo;
  1465.                 eInfo^^.editWind := editWind;
  1466.                 eInfo^^.scroll := editScroll;
  1467.                 eInfo^^.editTE := editTE;
  1468.                 eInfo^^.bound := bound;
  1469.                 eInfo^^.editFile := editFile;
  1470.  
  1471.  
  1472. {    Install default event notification procedures, font characteristics.}
  1473.  
  1474.                 SetEWindowProcs(editWind, e_key, e_activate, e_close);
  1475.                 SetEWindowStyle(editWind, e_font, e_size, e_wrap, e_just);
  1476.                 SetDirty(false);
  1477.  
  1478. {    If supposed to read file, do so.  Check the return value of}
  1479. {    Revert and toss the window if there was an error.}
  1480.  
  1481.                 IF bindToFile THEN
  1482.                     IF (Revert = false) THEN
  1483.                         BEGIN
  1484.                             SkelRmveWind(editWind);
  1485.                             SetPort(savePort);
  1486.                             failure := true;
  1487.                         END;
  1488.             END;
  1489.         IF NOT failure THEN
  1490.             BEGIN
  1491.  
  1492. {    Show window if specified as visible, and return a pointer to it.}
  1493.  
  1494.                 SyncGlobals(editWind);
  1495.                 OverhaulDisplay(true, true);
  1496.                 IF visible THEN
  1497.                     ShowWindow(editWind);
  1498.                 SetPort(savePort);
  1499.                 NewEWindow := editWind;
  1500.             END
  1501.         ELSE
  1502.             NewEWindow := NIL;
  1503.     END;
  1504.  
  1505. {    Look through the list of windows, shutting down all the edit}
  1506. {    windows.  If any window is dirty, ask user about saving it first.}
  1507. {    If the user cancels on any such request, ClobberEWindows returns}
  1508. {    false.  If all edit windows are shut down, return true.  It is}
  1509. {    then safe for the host to exit.}
  1510.  
  1511. {    When a window *is* shut down, have to start looking through the}
  1512. {    window list again, since theWind no longer points anywhere}
  1513. {    meaningful.}
  1514.  
  1515.     FUNCTION ClobberEWindows;
  1516.  
  1517.         VAR
  1518.             theWind : WindowPtr;
  1519.             breakflag, flag2 : Boolean;
  1520.             mypeek : WindowPeek;
  1521.  
  1522.     BEGIN
  1523.         breakflag := false;
  1524.         WHILE NOT breakflag DO
  1525.             BEGIN
  1526.                 theWind := FrontWindow;
  1527.                 flag2 := false;
  1528.                 WHILE (theWind <> NIL) AND NOT flag2 DO        { all edit windows are not shut down }
  1529.                     BEGIN
  1530.                         IF ISEWindow(theWind) THEN
  1531.                             flag2 := true
  1532.                         ELSE
  1533.                             BEGIN
  1534.                                 mypeek := WindowPeek(theWind);
  1535.                                 theWind := WindowPtr(mypeek^.nextWindow);
  1536.                             END;
  1537.                     END;
  1538.                 IF theWind = NIL THEN
  1539.                     BEGIN
  1540.                         ClobberEWindows := true;
  1541.                         breakflag := true;
  1542.                     END
  1543.                 ELSE
  1544.                     BEGIN
  1545.                         IF theWind <> FrontWindow THEN
  1546.                             BEGIN
  1547.                                 SelectWindow(theWind);
  1548.                                 ShowWindow(theWind);
  1549.                                 EWindowOverhaul(theWind, false, false, IsEWindowDirty(theWind));
  1550.                                 SetPort(theWind);
  1551.                                 ValidRect(theWind^.portRect);
  1552.                             END;
  1553.                         IF EWindowClose(theWind) = false THEN        { cancel or error }
  1554.                             BEGIN
  1555.                                 ClobberEWindows := false;
  1556.                                 breakflag := true;
  1557.                             END;
  1558.                     END;
  1559.             END;
  1560.     END;
  1561. END.